#include "stdafx.h"

#include "tdd.h"
#include "tddassert.h"
#include "utcommon.h"

#include "..\\SmartPtr\\Arena.h"

#define UNITSIZE 32

namespace SmartPtr
{
	namespace UnitTest
	{
		TESTCLASS(MemoryPoolTests)
		{
			TESTMETHOD(TestNewMemoryPool)
			{
				CArena memoryPool1(16);	
				TDD_VERIFY(memoryPool1.GetGrowSize() == 256);
				TDD_VERIFY(memoryPool1.GetUnitSize() == 16);
				TDD_VERIFY(memoryPool1.GetInitSize() == 1024);

				CArena memoryPool2(12);	
				TDD_VERIFY(memoryPool2.GetGrowSize() == 256);
				TDD_VERIFY(memoryPool2.GetUnitSize() == 16);
				TDD_VERIFY(memoryPool2.GetInitSize() == 1024);

				CArena memoryPool3(12, 1023, 245);	
				TDD_VERIFY(memoryPool3.GetGrowSize() == 248);
				TDD_VERIFY(memoryPool3.GetUnitSize() == 16);
				TDD_VERIFY(memoryPool3.GetInitSize() == 1024);
			}

			TESTMETHOD(TestAlloc)
			{
				CArena memoryPool1(UNITSIZE);	
				TDD_VERIFY(memoryPool1.GetGrowSize() == 256);
				TDD_VERIFY(memoryPool1.GetUnitSize() == UNITSIZE);
				TDD_VERIFY(memoryPool1.GetInitSize() == 1024);

				// there are totally 1024/UNITSIZE units
				// the last one is special for the next index is not specified
				for (int i = 0; i < 1024/UNITSIZE; i++)
				{
					short* p = (short*)memoryPool1.Alloc();
					TDD_VERIFY(memoryPool1.GetBlockCount() == 1);
					TDD_VERIFY(memoryPool1.FirstFreeUnitIndex(p) == i + 1);
					TDD_VERIFY(memoryPool1.GetFreeUnitCount(p) == 1024/UNITSIZE - i - 1);
				}

				// another alloc, which will cause new block be constructed
				short* p = (short*)memoryPool1.Alloc();
				TDD_VERIFY(memoryPool1.GetBlockCount() == 2);
				TDD_VERIFY(memoryPool1.FirstFreeUnitIndex(p) == 1);
				// now is the grow size
				TDD_VERIFY(memoryPool1.GetFreeUnitCount(p) == 256/UNITSIZE - 1);
			}

			TESTMETHOD(TestFree_FreeOneUnit)
			{
				CArena memoryPool1(UNITSIZE);

				///////////////////
				// one single unit
				short* p = (short*)memoryPool1.Alloc();
				TDD_VERIFY(memoryPool1.GetBlockCount() == 1);
				TDD_VERIFY(memoryPool1.FirstFreeUnitIndex(p) == 1);
				TDD_VERIFY(memoryPool1.GetFreeUnitCount(p) == 1024/UNITSIZE - 1);

				memoryPool1.Free(p);
				TDD_VERIFY(memoryPool1.GetBlockCount() == 1);
				TDD_VERIFY(memoryPool1.FirstFreeUnitIndex(p) == 0);
				TDD_VERIFY(memoryPool1.GetFreeUnitCount(p) == 1024/UNITSIZE);
			}

			TESTMETHOD(TestFree_FreeOneBlock)
			{
				CArena memoryPool1(UNITSIZE);

				///////////////////////
				// fill the block with alloc
				// there are totally 64 units
				short* pArray[1024/UNITSIZE];
				for (int i = 0; i < 1024/UNITSIZE; i++)
				{
					pArray[i] = (short*)memoryPool1.Alloc();
					TDD_VERIFY(memoryPool1.GetBlockCount() == 1);
					TDD_VERIFY(memoryPool1.FirstFreeUnitIndex(pArray[i]) == i + 1);
					TDD_VERIFY(memoryPool1.GetFreeUnitCount(pArray[i]) == 1024/UNITSIZE - i - 1);
				}
				
				for (int i = 0; i < 1024/UNITSIZE; i++)
				{
					memoryPool1.Free(pArray[i]);
					TDD_VERIFY(memoryPool1.GetBlockCount() == 1);
					TDD_VERIFY(memoryPool1.FirstFreeUnitIndex(pArray[i]) == i);
					TDD_VERIFY(memoryPool1.GetFreeUnitCount(pArray[i]) == i + 1);
					if (i > 0)
					{	
						TDD_VERIFY(memoryPool1.NextFreeUnitIndex(pArray[i]) == memoryPool1.GetUnitIndex(pArray[i - 1]));
					}
				}
			}

			TESTMETHOD(TestFree_FreeOneBlockAllocOneUnit)
			{
				CArena memoryPool1(UNITSIZE);

				///////////////////////
				// fill the block with alloc
				// there are totally 64 units
				short* pArray[1024/UNITSIZE];
				for (int i = 0; i < 1024/UNITSIZE; i++)
				{
					pArray[i] = (short*)memoryPool1.Alloc();
					TDD_VERIFY(memoryPool1.GetBlockCount() == 1);
					TDD_VERIFY(memoryPool1.FirstFreeUnitIndex(pArray[i]) == i + 1);
					TDD_VERIFY(memoryPool1.GetFreeUnitCount(pArray[i]) == 1024/UNITSIZE - i - 1);
				}

				for (int i = 0; i < 1024/UNITSIZE; i++)
				{
					memoryPool1.Free(pArray[i]);
					TDD_VERIFY(memoryPool1.GetBlockCount() == 1);
					TDD_VERIFY(memoryPool1.FirstFreeUnitIndex(pArray[i]) == i);
					TDD_VERIFY(memoryPool1.GetFreeUnitCount(pArray[i]) == i + 1);
					if (i > 0)
					{
						TDD_VERIFY(memoryPool1.NextFreeUnitIndex(pArray[i]) == memoryPool1.GetUnitIndex(pArray[i - 1]));
					}
				}

				// now alloc a new unit
				// the last delete is index 31, so the alloc will apply the 31 to the "p"
				short* p = (short*)memoryPool1.Alloc();
				TDD_VERIFY(memoryPool1.GetBlockCount() == 1);
				// the last second deletion is 30, so the index 31 will pointer to index 30
				TDD_VERIFY(memoryPool1.FirstFreeUnitIndex(p) == 30);
				TDD_VERIFY(memoryPool1.GetFreeUnitCount(p) == 1024/UNITSIZE - 1);
			}

			TESTMETHOD(TestFree_AddOneBlockAndOneUnit_FreeTheLastUnit)
			{
				CArena memoryPool1(UNITSIZE);

				////////////////
				// add another group and one extra block
				// there are totally 64 units
				for (int i = 0; i < 1024/UNITSIZE; i++)
				{
					short* p = (short*)memoryPool1.Alloc();
				}

				// another alloc, which will cause new block be constructed
				short* p = (short*)memoryPool1.Alloc();
				
				// extra block free
				memoryPool1.Free(p);
				TDD_VERIFY(memoryPool1.GetBlockCount() == 1);
				// the following two will cause error, for the block containing "p" is deleted
				//TDD_VERIFY(memoryPool1.FirstFreeUnitIndex(p) == 1);
				//TDD_VERIFY(memoryPool1.GetFreeUnitCount(p) == 256/16);
			}

			TESTMETHOD(TestFree_Erase2Unit)
			{
				CArena memoryPool1(UNITSIZE);
				short* pArray[1024/UNITSIZE] = {NULL};

				////////////////
				// add another group and extract some units in the middle
				// there are totally 64 units
				for (int i = 0; i < 1024/UNITSIZE; i++)
				{
					pArray[i] = (short*)memoryPool1.Alloc();
				}

				// extra block free
				memoryPool1.Free(pArray[5]);
				TDD_VERIFY(memoryPool1.GetBlockCount() == 1);
				TDD_VERIFY(memoryPool1.FirstFreeUnitIndex(pArray[5]) == 5);
				// there should be only one free unit in the block containing pArray[5]
				TDD_VERIFY(memoryPool1.GetFreeUnitCount(pArray[5]) == 1);
				TDD_VERIFY(memoryPool1.NextFreeUnitIndex(pArray[5]) == 1024/UNITSIZE);

				// extra block free
				memoryPool1.Free(pArray[8]);
				TDD_VERIFY(memoryPool1.GetBlockCount() == 1);
				TDD_VERIFY(memoryPool1.FirstFreeUnitIndex(pArray[8]) == 8);
				TDD_VERIFY(memoryPool1.GetFreeUnitCount(pArray[8]) == 2);
				// we first erase pArray[5], and then pArray[8], so the pArray[8]'s position
				// stores the the next free unit, it should be pArray[5]'s index
				TDD_VERIFY(memoryPool1.NextFreeUnitIndex(pArray[8]) == memoryPool1.GetUnitIndex(pArray[5]));

				// extra block free
				memoryPool1.Free(pArray[10]);
				TDD_VERIFY(memoryPool1.GetBlockCount() == 1);
				TDD_VERIFY(memoryPool1.FirstFreeUnitIndex(pArray[10]) == 10);
				TDD_VERIFY(memoryPool1.GetFreeUnitCount(pArray[10]) == 3);
				TDD_VERIFY(memoryPool1.NextFreeUnitIndex(pArray[10]) == memoryPool1.GetUnitIndex(pArray[8]));

			}

		};
	}
}